Using Geometric Objects
QuickDraw 3D provides routines that you can use to create and edit geometric objects, get and set attributes for those objects, and perform other geometric operations. This section illustrates how to create and delete some geometric objects and how to traverse the parts of a mesh.Creating and Deleting Geometric Objects
As you saw briefly in the chapter "Introduction to QuickDraw 3D," QuickDraw 3D supports both immediate and retained modes of defining and rendering a model. Which mode you employ in any particular instance depends on the needs of your application. As suggested earlier, if much of the model remains unchanged from frame to frame, you should use retained mode imaging to create and draw the model. If, however, many parts of the model do change from frame to frame, you should use immediate mode imaging, creating and rendering a model on a shape-by-shape basis.Listing 4-1 illustrates how to create a retained box.
Listing 4-1 Creating a retained box
TQ3GeometryObject myBox; TQ3BoxData myBoxData; Q3Point3D_Set(&myBoxData.origin, 1.0, 1.0, 1.0); Q3Vector3D_Set(&myBoxData.orientation, 0, 2.0, 0); Q3Vector3D_Set(&myBoxData.minorAxis, 2.0, 0, 0); Q3Vector3D_Set(&myBoxData.majorAxis, 0, 0, 2.0); myBox = Q3Box_New(&myBoxData);Once the code in Listing 4-1 has been executed, the variablemyBox
contains a reference to the new box. You can then reuse or dispose of themyBoxData
structure, because all subsequent operations on the retained box are performed usingmyBox
. For example, to submit the box for drawing, picking, bounding, or writing, you can execute the following line of code inside a rendering, picking, bounding, or writing loop:
myStatus = Q3Object_Submit(myBox, myView);To dispose of the retained box, you can call theQ3Object_Dispose
function,
as follows:
myStatus = Q3Object_Dispose(myBox);Listing 4-2 illustrates how to create an immediate box.Listing 4-2 Creating an immediate box
TQ3BoxData myBoxData; Q3Point3D_Set(&myBoxData.origin, 1.0, 1.0, 1.0); Q3Vector3D_Set(&myBoxData.orientation, 0, 2.0, 0); Q3Vector3D_Set(&myBoxData.minorAxis, 2.0, 0, 0); Q3Vector3D_Set(&myBoxData.majorAxis, 0, 0, 2.0);As you can see, you do not have to call any QuickDraw 3D routine to create an immediate box; instead, you simply define the box data in a structure of typeTQ3BoxData
. To draw an immediate box, you call theQ3Box_Submit
function (inside a rendering loop), as follows:
myStatus = Q3Box_Submit(myBox, myView);Because you didn't create any retained entity, you do not need to dispose of the immediate box.Creating a Mesh
As you saw earlier (in "Meshes," beginning on page 4-6), you create a mesh
by callingQ3Mesh_New
to create a new empty mesh and then by callingQ3Mesh_VertexNew
andQ3Mesh_FaceNew
to explicitly add vertices and faces
to the mesh. Listing 4-3 illustrates how to create a simple mesh using these functions. It also shows how to attach a custom surface parameterization to
a mesh face, so that a texture can be mapped onto the face.Listing 4-3 Creating a simple mesh
TQ3GroupObject MyBuildMesh (void) { TQ3ColorRGB myMeshColor; TQ3GroupObject myModel; static TQ3Vertex3D vertices[9] = { { { -0.5, 0.5, 0.0 }, NULL }, { { -0.5, -0.5, 0.0 }, NULL }, { { 0.0, -0.5, 0.3 }, NULL }, { { 0.5, -0.5, 0.0 }, NULL }, { { 0.5, 0.5, 0.0 }, NULL }, { { 0.0, 0.5, 0.3 }, NULL }, { { -0.4, 0.2, 0.0 }, NULL }, { { 0.0, 0.0, 0.0 }, NULL }, { { -0.4, -0.2, 0.0 }, NULL }}; static TQ3Param2D verticesUV[9] = { {0.0, 1.0}, {0.0, 0.0}, {0.5, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.5, 1.0}, {0.1, 0.8}, {0.5, 0.5}, {0.1, 0.4}}; TQ3MeshVertex myMeshVertices[9]; TQ3GeometryObject myMesh; TQ3MeshFace myMeshFace; TQ3AttributeSet myFaceAttrs; unsigned long i; myMesh = Q3Mesh_New(); /*create new empty mesh*/ Q3Mesh_DelayUpdates(myMesh);/*turn off mesh updating*/ /*Add vertices and surface parameterization to mesh.*/ for (i = 0; i < 9; i++) { TQ3AttributeSet myVertAttrs; myMeshVertices[i] = Q3Mesh_VertexNew(myMesh, &vertices[i]); myVertAttrs = Q3AttributeSet_New(); Q3AttributeSet_Add (myVertAttrs, kQ3AttributeTypeSurfaceUV, &verticesUV[i]); Q3Mesh_SetVertexAttributeSet(myMesh, myMeshVertices[i], myVertAttrs); Q3Object_Dispose(myVertAttrs); } myFaceAttrs = Q3AttributeSet_New(); myMeshColor.r = 0.3; myMeshColor.g = 0.9; myMeshColor.b = 0.5; Q3AttributeSet_Add (myFaceAttrs, kQ3AttributeTypeDiffuseColor, &myMeshColor); myMeshFace = Q3Mesh_FaceNew(myMesh, 6, myMeshVertices, myFaceAttrs); Q3Mesh_FaceToContour(myMesh, myMeshFace, Q3Mesh_FaceNew(myMesh, 3, &myMeshVertices[6], NULL)); Q3Mesh_ResumeUpdates(myMesh); myModel = Q3OrderedDisplayGroup_New(); Q3Group_AddObject(myModel, myMesh); Q3Object_Dispose(myFaceAttrs); Q3Object_Dispose(myMesh); return (myModel); }The new mesh created by MyBuildMesh is a retained object. Note that you need to callQ3Mesh_New
before you callQ3Mesh_VertexNew
andQ3Mesh_FaceNew
. Also, the call toQ3Mesh_FaceToContour
destroys any attributes associated with the mesh face that is turned into a contour.Traversing a Mesh
QuickDraw 3D supplies a large number of functions that you can use to traverse a mesh by iterating through the various distinguishable parts of the mesh (that is, through the faces, vertices, edges, contours, or components in the mesh). For example, you can operate on each face of a mesh by calling theQ3Mesh_FirstMeshFace
function to get the first face in the mesh and then by callingQ3Mesh_NextMeshFace
to get each successive face in the mesh. When you callQ3Mesh_FirstMeshFace
, you specify a mesh and a mesh iterator structure, which QuickDraw 3D fills in with information about its current position while traversing a mesh. You must pass that same mesh iterator structure toQ3Mesh_NextMeshFace
when you get successive faces in the mesh. Listing 4-4 illustrates how to use these routines to operate on all faces in a mesh.Listing 4-4 Iterating through all faces in a mesh
TQ3Status MySetMeshFacesDiffuseColor (TQ3GeometryObject myMesh, TQ3ColorRGB color) { TQ3MeshFace myFace; TQ3MeshIterator myIter; TQ3Status myErr; TQ3AttributeSet mySet; for (myFace = Q3Mesh_FirstMeshFace(myMesh, &myIter); myFace; myFace = Q3Mesh_NextMeshFace(&myIter)) { /*Get the current attribute set of the current face.*/ myErr = Q3Mesh_GetFaceAttributeSet(myMesh, myFace, &mySet); if (myErr == kQ3Failure) return (kQ3Failure); /*Add the color attribute to the face attribute set.*/ myErr = Q3AttributeSet_Add((TQ3AttributeSet)mySet, kQ3AttributeTypeDiffuseColor, &color); if (myErr == kQ3Failure) return (kQ3Failure); /*Set the attribute set of the current face.*/ myErr = Q3Mesh_SetFaceAttributeSet(myMesh, myFace, mySet); if (myErr == kQ3Failure) return (kQ3Failure); } return (kQ3Success); }QuickDraw 3D also supplies a number of C language macros that you can use to simplify your source code. For example, you can use theQ3ForEachMeshFace
macro, defined like this:
#define Q3ForEachMeshFace(m,f,i) \ for ( (f) = Q3Mesh_FirstMeshFace((m),(i)); \ (f); \ (f) = Q3Mesh_NextMeshFace((i)) )Listing 4-5 shows how to use two of these macros to attach a corner to each vertex or each face of a mesh.Listing 4-5 Attaching corners to all vertices in all faces of a mesh
TQ3Status MyAddCornersToMesh (TQ3GeometryObject myMesh, TQ3AttributeSet mySet) { TQ3MeshFace myFace; TQ3MeshVertex myVertex; TQ3MeshIterator myIter1; TQ3MeshIterator myIter2; TQ3Status myErr; Q3ForEachMeshFace(myMesh, myFace, &myIter1) { Q3ForEachFaceVertex(myFace, myVertex, &myIter2) { myErr = Q3Mesh_SetCornerAttributeSet (myMesh, myFace, myVertex, mySet); if (myErr == kQ3Failure) return (kQ3Failure); } } return (kQ3Success); }>